﻿namespace System.IO.IsolatedStorage
{
    /// <summary>
    /// <see cref="IsolatedStorageSettings">Isolated Storage Settings</see> extensions.
    /// </summary>
    public static class Extensions
    {
        /// <summary>
        /// Gets value from <see cref="IsolatedStorageSettings">Isolated Storage Settings</see> if exists, otherwise returns null (for Reference Types) or default (for Value Types).
        /// </summary>
        /// <typeparam name="T">Type of value to be casted to.</typeparam>
        /// <param name="settings"><see cref="IsolatedStorageSettings">Isolated Storage Settings</see>.</param>
        /// <param name="key">Settings key.</param>
        /// <returns>Value.</returns>
        public static T GetValueIfExists<T>(this IsolatedStorageSettings settings, string key)
            where T : struct
        {
            Validate(settings, key);

            return settings.Contains(key) ? ((T) settings[key]) : default(T);
        }

        /// <summary>
        /// Gets value from <see cref="IsolatedStorageSettings">Isolated Storage Settings</see> if exists, otherwise returns null.
        /// </summary>
        /// <typeparam name="T">Type of value to be casted to.</typeparam>
        /// <param name="settings"><see cref="IsolatedStorageSettings">Isolated Storage Settings</see>.</param>
        /// <param name="key">Settings key.</param>
        /// <returns>Value.</returns>
        public static T? GetNullableValueIfExists<T>(this IsolatedStorageSettings settings, string key)
            where T : struct
        {
            Validate(settings, key);

            return settings.Contains(key) ? (T?)((T)settings[key]) : null;
        }

        /// <summary>
        /// Gets object from <see cref="IsolatedStorageSettings">Isolated Storage Settings</see> if exists, otherwise returns null.
        /// </summary>
        /// <typeparam name="T">Type of object to be casted to.</typeparam>
        /// <param name="settings"><see cref="IsolatedStorageSettings">Isolated Storage Settings</see>.</param>
        /// <param name="key">Settings key.</param>
        /// <returns>Value.</returns>
        public static T GetObjectIfExists<T>(this IsolatedStorageSettings settings, string key)
            where T : class
        {
            Validate(settings, key);

            return settings.Contains(key) ? ((T) settings[key]) : null;
        }

        /// <summary>
        /// Saves value to the <see cref="IsolatedStorageSettings">Isolated Storage Settings</see> or updates if key already found.
        /// </summary>
        /// <typeparam name="T">Type of value.</typeparam>
        /// <param name="settings"><see cref="IsolatedStorageSettings">Isolated Storage Settings</see>.</param>
        /// <param name="key">Key.</param>
        /// <param name="value">Value</param>
        public static void SaveOrUpdateValue<T>(this IsolatedStorageSettings settings, string key, T value)
        {
            Validate(settings, key);

            if (!settings.Contains(key))
            {
                settings.Add(key, value);
            }
            else
            {
                settings[key] = value;
            }

            settings.Save();
        }

        private static void Validate(IsolatedStorageSettings settings, string key)
        {
            if (settings == null)
            {
                throw new ArgumentNullException("settings");
            }

            if (string.IsNullOrEmpty("key"))
            {
                throw new ArgumentException(string.Empty, key);
            }
        }
    }
}
